package tiltmaze.view;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import tiltmaze.Cell;
import tiltmaze.Maze;

/**
 * Interactive graphical representation of a maze.
 * @author Juraj Niznan
 */
public class MazeCanvas extends Component {

    public static int SQUARE_SIZE = 10;
    public static int BORDER = 20;
    private Maze maze;
    private MazeRectangle[][] verticalRectangles;
    private MazeRectangle[][] horizontalRectangles;
    private MazeRectangle[][] checkpoints;
    private int width;
    private int height;
    private MazeRectangle hovRect;
    private Ball ball;

    /**
     * Constructor. Creates graphical representation according to the maze provided.
     * @param maze
     */
    public MazeCanvas(Maze maze) {
        this.maze = maze;
        width = maze.getNumOfCols();
        height = maze.getNumOfRows();
        Cell[][] cells = maze.getCells();
        verticalRectangles = new MazeRectangle[height][width + 1];
        horizontalRectangles = new MazeRectangle[height + 1][width];
        checkpoints = new MazeRectangle[height][width];
        for (int i = 0; i < height; i++) {
            for (int j = 0; j <= width; j++) {
                verticalRectangles[i][j] = new MazeRectangle(i, j, MazeRectangle.VERTICAL_WALL);
                verticalRectangles[i][j].visible = (j < width) ? cells[i][j].getWalls()[3] : cells[i][j - 1].getWalls()[1];
            }
        }
        for (int i = 0; i <= height; i++) {
            for (int j = 0; j < width; j++) {
                horizontalRectangles[i][j] = new MazeRectangle(i, j, MazeRectangle.HORIZONTAL_WALL);
                horizontalRectangles[i][j].visible = (i < height) ? cells[i][j].getWalls()[0] : cells[i - 1][j].getWalls()[2];
            }
        }
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                checkpoints[i][j] = new MazeRectangle(i, j, MazeRectangle.CHECKPOINT);
                checkpoints[i][j].visible = cells[i][j].getCheckpoint();
                if (cells[i][j].getStart()) {
                    ball = new Ball(i, j, width, height);
                }
            }
        }
        this.setBackground(Color.white);
        this.setPreferredSize(new Dimension(BORDER * 2 + SQUARE_SIZE * (5 * width + 1), BORDER * 2 + SQUARE_SIZE * (5 * height + 1)));
        this.addMouseMotionListener(mouseMotionListener);
        this.addMouseListener(mouseListener);
    }

    @Override
    public void paint(Graphics graphics) {
        Graphics2D g = (Graphics2D) graphics;

        g.setColor(getBackground());
        g.fillRect(0, 0, getWidth(), getHeight());

        //draw wall outlines:
        g.setColor(Color.LIGHT_GRAY);
        for (int i = 0; i < height; i++) {
            for (int j = 0; j <= width; j++) {
                g.draw(verticalRectangles[i][j]);

            }
        }
        for (int i = 0; i <= height; i++) {
            for (int j = 0; j < width; j++) {
                g.draw(horizontalRectangles[i][j]);
            }
        }
        //draw walls:
        for (int i = 0; i < height; i++) {
            for (int j = 0; j <= width; j++) {
                if (verticalRectangles[i][j].hovered && verticalRectangles[i][j].visible) {
                    g.setColor(Color.GRAY);
                    g.fill(verticalRectangles[i][j]);
                } else if (verticalRectangles[i][j].hovered) {
                    g.setColor(Color.LIGHT_GRAY);
                    g.fill(verticalRectangles[i][j]);
                }
            }
        }
        for (int i = 0; i <= height; i++) {
            for (int j = 0; j < width; j++) {
                if (horizontalRectangles[i][j].hovered && horizontalRectangles[i][j].visible) {
                    g.setColor(Color.GRAY);
                    g.fill(horizontalRectangles[i][j]);
                } else if (horizontalRectangles[i][j].hovered) {
                    g.setColor(Color.LIGHT_GRAY);
                    g.fill(horizontalRectangles[i][j]);
                } 
            }
        }
        //draw black walls:
        for (int i = 0; i < height; i++) {
            for (int j = 0; j <= width; j++) {
                if (!verticalRectangles[i][j].hovered && verticalRectangles[i][j].visible) {
                    g.setColor(Color.BLACK);
                    g.fill(verticalRectangles[i][j]);
                }

            }
        }
        for (int i = 0; i <= height; i++) {
            for (int j = 0; j < width; j++) {
                if (!horizontalRectangles[i][j].hovered && horizontalRectangles[i][j].visible) {
                    g.setColor(Color.BLACK);
                    g.fill(horizontalRectangles[i][j]);
                }
            }
        }
        //draw checkpoint:
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                if (checkpoints[i][j].hovered && checkpoints[i][j].visible) {
                    g.setColor(Color.GRAY);
                    g.fill(checkpoints[i][j]);
                } else if (checkpoints[i][j].hovered) {
                    g.setColor(Color.LIGHT_GRAY);
                    g.fill(checkpoints[i][j]);
                } else if (checkpoints[i][j].visible) {
                    g.setColor(Color.BLUE);
                    g.fill(checkpoints[i][j]);
                }
            }
        }
        //draw ball:
        g.setColor(Color.RED);
        g.fill(ball);

    }
    private MouseMotionListener mouseMotionListener = new MouseMotionListener() {

        public void mouseDragged(MouseEvent e) {
            ball.drag(e.getPoint());
            if (hovRect != null) {
                if (!hovRect.contains(e.getPoint())) {
                    hovRect.hovered = false;
                    hovRect = null;
                }
            }
            e.getComponent().repaint();
        }

        public void mouseMoved(MouseEvent e) {
            Point p = e.getPoint();
            hovRect = null;
            for (int i = 0; i < height; i++) {
                for (int j = 1; j < width; j++) {
                    verticalRectangles[i][j].hovered = verticalRectangles[i][j].contains(p);
                    hovRect = (verticalRectangles[i][j].hovered) ? verticalRectangles[i][j] : hovRect;
                }
            }
            for (int i = 1; i < height; i++) {
                for (int j = 0; j < width; j++) {
                    horizontalRectangles[i][j].hovered = horizontalRectangles[i][j].contains(p);
                    hovRect = (horizontalRectangles[i][j].hovered) ? horizontalRectangles[i][j] : hovRect;
                }
            }
            for (int i = 0; i < height; i++) {
                for (int j = 0; j < width; j++) {
                    checkpoints[i][j].hovered = checkpoints[i][j].contains(p);
                    hovRect = (checkpoints[i][j].hovered) ? checkpoints[i][j] : hovRect;
                }
            }
            if (ball.contains(p) && hovRect != null) {
                hovRect.hovered = false;
                hovRect = null;
            }
            e.getComponent().repaint();
        }
    };
    private MouseListener mouseListener = new MouseAdapter() {

        @Override
        public void mousePressed(MouseEvent e) {
            ball.tryDrag(e.getPoint());
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            if (hovRect != null) {
                hovRect.visible = !hovRect.visible;
                if (hovRect.getType().equals(MazeRectangle.CHECKPOINT)) {
                    maze.setCheckpoint(hovRect.getRow(), hovRect.getCol(), hovRect.visible);
                }
                if (hovRect.getType().equals(MazeRectangle.VERTICAL_WALL)) {
                    maze.setVerticalWall(hovRect.getRow(), hovRect.getCol(), hovRect.visible);
                }
                if (hovRect.getType().equals(MazeRectangle.HORIZONTAL_WALL)) {
                    maze.setHorizontalWall(hovRect.getRow(), hovRect.getCol(), hovRect.visible);
                }
            }
            ball.stopDrag();
            maze.setStart(ball.getRow(), ball.getCol());
            e.getComponent().repaint();
        }
    };

    public Maze getMaze() {
        return maze;
    }


}
